<script lang="ts" setup>
import gsap from "gsap";
import { showToast } from "@nutui/nutui";
import "@nutui/nutui/dist/packages/toast/style";
import QuestionItem from "./question-item.vue";
import FeedbackPopup from "./feedback-popup.vue";
import { PracticeMode, Question, QuestionState, QuestionOptionIndex, UpdateActionRecordParams } from "@/types/practice";

const props = defineProps<{
  mode: PracticeMode;
  questionIds: Question["id"][];
  questionsStateMap: Map<Question["id"], QuestionState>;
  questionChoosedAnswerMap: Map<Question["id"], QuestionOptionIndex[]>;
  currentIndex: number;
}>();

const emit = defineEmits<{
  (e: "update:currentIndex", value: number): void;
  (e: "end"): void;
  (e: "chooseAnswer", questionId: Question["id"], optionIndexList: QuestionOptionIndex[], isCorrect: boolean): void;
  (e: "updateActionRecord", params: UpdateActionRecordParams): void;
}>();

// 空白的题目模板
const questionTemplate: QuestionState = {
  loadState: "silent",
  actionRecord: null,
  question: null,
  isCollect: false,
};

// 正在展示的三个题目
const showingQuestionState = computed(() =>
  [props.currentIndex - 1, props.currentIndex, props.currentIndex + 1].map((index) => {
    if (index < 0 || index >= props.questionIds.length) return questionTemplate;
    const id = props.questionIds[index];
    return props.questionsStateMap.get(id) || questionTemplate;
  })
);

// 滚动相关
const wrapperRef = ref<HTMLDivElement | null>(null);
const scrollData = reactive({
  touchStartX: 0,
  touchStartY: 0,
  startX: 0,
  startY: 0,
  currentX: 0,
  touching: false,
  animationTimeline: null as gsap.core.Tween | null,
});

let wrapperWidth = 0;
// 初始化滚动位置
onMounted(() => {
  wrapperWidth = wrapperRef.value?.clientWidth || 0;
  scrollData.currentX = -wrapperWidth;
});

// 手指按下
const handleWrapperTouchStart = (e: TouchEvent) => {
  scrollData.touching = true;
  scrollData.touchStartX = e.touches[0].clientX;
  scrollData.touchStartY = e.touches[0].clientY;
  scrollData.startX = scrollData.currentX;
  // 取消先前的动画
  scrollData.animationTimeline?.kill();
};

// 手指移动
const handleWrapperTouchMove = (e: TouchEvent) => {
  const diffX = e.touches[0].clientX - scrollData.touchStartX;
  const diffY = e.touches[0].clientY - scrollData.touchStartY;

  if (Math.abs(diffX) < Math.abs(diffY)) {
    handleWrapperTouchEnd();
  }

  if (!scrollData.touching) return;
  e.preventDefault();
  // 更新滑动位置
  scrollData.currentX = scrollData.startX + diffX;
};

// 复位动画
const resetPositionWithAnimation = () => {
  scrollData.animationTimeline = gsap.to(scrollData, {
    currentX: -wrapperWidth,
    duration: 0.3,
  });
};

// 吐司的配置
const toastOptions = {
  duration: 1000,
  "bg-color": "rgba(0, 0, 0, 0.7)",
};

// 跳转上一题
const toPreviousQuestion = () => {
  if (props.currentIndex > 0) {
    emit("update:currentIndex", props.currentIndex - 1);
    scrollData.currentX -= wrapperWidth;
  } else {
    showToast.text("已经是第一条啦！", toastOptions);
  }
};

// 跳转上一题
const toNextQuestion = () => {
  if (props.currentIndex < props.questionIds.length - 1) {
    emit("update:currentIndex", props.currentIndex + 1);
    scrollData.currentX += wrapperWidth;
  } else {
    emit("end");
  }
};

// 手指离开
const handleWrapperTouchEnd = () => {
  scrollData.touching = false;

  // 触发切换题目的距离
  const triggerPercentage = 0.2;

  // 检测滑动位置
  if (scrollData.currentX > -wrapperWidth * (1 - triggerPercentage))
    // 上一题
    toPreviousQuestion();
  else if (scrollData.currentX < -wrapperWidth * (1 + triggerPercentage))
    // 下一题
    toNextQuestion();

  resetPositionWithAnimation();
};

// 题目答对
const handleItemCorrect = () => {
  setTimeout(() => {
    toNextQuestion();
    resetPositionWithAnimation();
  }, 500);
};

// 图片预览
const imgPreviewVisible = ref(false);
const previewImgs = ref<{ src: string }[]>([]);

const previewImgFn = (imgUrl: string) => {
  previewImgs.value = [{ src: imgUrl }];
  imgPreviewVisible.value = true;
};

const imgPreviewCloseFn = () => {
  imgPreviewVisible.value = false;
};

// 反馈弹窗
const feedbackPopupRef = ref<typeof FeedbackPopup>();
const showFeedbackPopup = (questionId: Question["id"]) => {
  feedbackPopupRef.value?.open(questionId);
};
</script>

<template>
  <div
    @touchstart="handleWrapperTouchStart"
    @touchmove="handleWrapperTouchMove"
    @touchend="handleWrapperTouchEnd"
    @touchcancel="handleWrapperTouchEnd"
  >
    <div ref="wrapperRef" class="absolute inset-0 flex" :style="{ transform: `translateX(${scrollData.currentX}px)` }">
      <QuestionItem
        v-for="(item, index) in showingQuestionState"
        :key="index"
        :current-index="props.currentIndex"
        :mode="props.mode"
        :question-state="item"
        :choosed-answer="props.questionChoosedAnswerMap.get(item.question?.id || '')"
        @preview-img="previewImgFn"
        @feedback="item.question && showFeedbackPopup(item.question.id)"
        @correct="handleItemCorrect"
        @choose-answer="(optionIndexList, isCorrect) => emit('chooseAnswer', item.question!.id, optionIndexList, isCorrect)"
        @update-action-record="(params) => emit('updateActionRecord', params)"
      />
    </div>

    <!-- 图片预览 -->
    <nut-image-preview :show="imgPreviewVisible" :images="previewImgs" @close="imgPreviewCloseFn" />

    <!-- 反馈弹窗 -->
    <feedback-popup ref="feedbackPopupRef" />
  </div>
</template>
